package com.demo.config.apache.util;

import cn.hutool.core.util.StrUtil;
import com.demo.config.apache.util.word.FontStyleEnum;
import com.demo.config.apache.util.word.WordBaseConstants;
import opennlp.tools.util.StringUtil;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.model.PicturesTable;
import org.apache.poi.hwpf.model.StyleDescription;
import org.apache.poi.hwpf.model.StyleSheet;
import org.apache.poi.hwpf.usermodel.*;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.impl.xb.xmlschema.SpaceAttribute;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.officeDocument.x2006.sharedTypes.STOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.*;
import java.math.BigInteger;
import java.nio.file.Files;
import java.util.*;

/**
 * 以下包含功能： 1. 提取doc段落样式名称 标题、目录、正文等 2. 段落提取doc样式名称 标题、目录、正文等 3. 提取doc标题 4. 提取docx标题 5.
 * 提取doc文本 6. 提取docx文本 7. 提取doc字体大小 8. 文本块提取docx字体大小 9. 提取doc字体是否加粗 10. 文本块提取docx字体是否加粗
 * 11. 提取doc字体名称 12. 提取docx字体名称 13. word中的excel内容打印 doc 14. word中的excel内容打印 docx 15.
 * DOC按照区间层次打印文件的文本 指定开始中文及结束中文及样式 16. DOCX按照区间层次打印文件的文本 指定开始中文及结束中文及样式 17.
 * 提取doc中的图片-保存到指定文件 18. 提取docx中的图片-保存到指定文件 19. 添加一个段落-docx 20. docx设置excel 21.
 * docx给excel填充文本 22. docx合并单元格 23. docx导出word文档 24. docx替换文本内容 25. docx设置字体样式 26.
 * doc、docx修改文本样式 27. docx读取模板样式-获取模板的内容及样式、替换目标相同内容的样式 28. docx设置纸张大小 29. docx设置页边距
 *
 * @author wgr
 */
public class WordUtil {

	/**
	 * 提取标题，注意可能存在 *不同字体/字体大小/加粗等等* 的正文内容当做标题使用的情形 docx/doc doc/docx 通过默认格式 标题/正文 提取
	 * 通过字体大小以及是否加粗为标题提取
	 * @param filePath 文件地址
	 * @throws IOException 异常
	 */
	public static void printWord(String filePath) throws IOException {
		InputStream is = new FileInputStream(filePath);
		if (filePath.endsWith(".doc")) {
			HWPFDocument doc = new HWPFDocument(is);
			// 文档范围
			Range r = doc.getRange();
			// 标题
			StringBuilder title = new StringBuilder();
			// 正文
			StringBuilder straight = new StringBuilder();
			// 标题集合
			StringBuilder titles = new StringBuilder();
			for (int i = 0; i < r.numParagraphs(); i++) {
				// 获取段落
				// 获取第 i 个段落
				Paragraph p = r.getParagraph(i);
				// 获取段落样式索引
				int styleIndex = p.getStyleIndex();
				// 获取样式表
				StyleSheet styleSheet = doc.getStyleSheet();
				// 通过样式索引获取样式描述
				StyleDescription style = styleSheet.getStyleDescription(styleIndex);
				// 获取样式名称
				String styleName = style.getName();

				if (StrUtil.isNotBlank(styleName) && StrUtil.isNotBlank(p.text())) {
					// 标题集合
					titles.append(styleName);
					// 打印各段落的格式
					// System.out.println(i+","+styleIndex+","+styleName);
					if (styleName.contains("标题")) {
						title.append("、").append(p.text());
					}
					else {
						straight.append("、").append(p.text());
						// System.out.println("段落第"+i+"个有："+p.text());
					}
					// ===========================================================
					CharacterRun characterRun = p.getCharacterRun(0);
					// 字体
					String fontName = characterRun.getFontName();
					// 字体大小
					int fontSize = characterRun.getFontSize();
					int color = characterRun.getColor();
					// 是否加粗
					boolean bold = characterRun.isBold();
					System.out.println("字体大小：" + fontSize + "：：字体文本：：" + p.text());
					// ===========================================================

				}

			}
			System.out.println("文章标题有：" + checkText(title.toString()));
			System.out.println("文章正文有：" + checkText(straight.toString()));

			doc.close();
		}
		else if (filePath.endsWith(".docx")) {

			// 创建一个新的XWPFDocument对象，从输入流is中读取内容
			XWPFDocument doc = new XWPFDocument(is);
			// 获取段落迭代器
			Iterator<XWPFParagraph> itPara = doc.getParagraphsIterator();
			// 标题
			StringBuilder title = new StringBuilder();
			// 正文
			StringBuilder straight = new StringBuilder();
			XWPFStyles styles = doc.getStyles();
			// 遍历段落
			while (itPara.hasNext()) {
				// 获取下一个段落
				XWPFParagraph paragraph = itPara.next();
				// 获取段落中的所有运行
				List<XWPFRun> runs = paragraph.getRuns();
				// 获取段落样式
				String styleID = paragraph.getStyleID();

				String text = paragraph.getText();
				String name = "";
				if (StrUtil.isNotBlank(styleID)) {
					XWPFStyle style1 = styles.getStyle(styleID);
					// name 格式太多 太杂
					name = style1.getName();
				}

				if (StrUtil.isNotBlank(text)) {
					if (StrUtil.isNotBlank(name) && (name.contains("标题") || name.contains("heading"))) {
						title.append("、").append(text);
					}
					else {
						straight.append("、").append(text);
					}
				}
			}
			System.out.println("文章标题有：" + checkText(title.toString()));
			System.out.println("文章正文有：" + checkText(straight.toString()));
			doc.close();
		}
		else {
			System.out.println("不是word格式");
		}
	}

	/**
	 * 按照区间层次打印文件的文本 指定开始中文及结束中文及样式
	 * @param filePath 文件地址
	 * @throws IOException 异常
	 */
	public static void printWord2(String filePath) throws IOException {
		InputStream is = new FileInputStream(filePath);
		if (filePath.endsWith(".doc")) {
			HWPFDocument doc = new HWPFDocument(is);
			Range r = doc.getRange();// 文档范围
			// 标题
			StringBuilder title = new StringBuilder();
			// 正文
			StringBuilder straight = new StringBuilder(16);
			// 标题集合
			HashSet<String> titleSets = new HashSet<>();
			// 保存数据的开关
			boolean isTrue = true;
			for (int i = 0; i < r.numParagraphs(); i++) {
				// 获取第 i 个段落
				Paragraph p = r.getParagraph(i);
				// 获取段落样式索引
				int styleIndex = p.getStyleIndex();
				// 获取样式表
				StyleSheet styleSheet = doc.getStyleSheet();
				// 通过样式索引获取样式描述
				StyleDescription style = styleSheet.getStyleDescription(styleIndex);
				// 获取样式名称
				String styleName = style.getName();
				if (StrUtil.isNotBlank(p.text())) {

					if (!styleName.contains("目录") && !p.text().contains("一、投标函") && p.text().contains("二、法定代表人身份证明")) {
						isTrue = false;
						break;
					}
					if (isTrue) {
						straight.append(p.text());
					}
				}

			}
			System.out.println("文章正文有：" + checkText(straight.toString()));
			doc.close();
		}
		else if (filePath.endsWith(".docx")) {

			// 创建一个新的XWPFDocument对象，从输入流is中读取内容
			XWPFDocument doc = new XWPFDocument(is);
			// 获取段落迭代器
			Iterator<XWPFParagraph> itPara = doc.getParagraphsIterator();
			// 标题
			StringBuilder title = new StringBuilder();
			// 正文
			StringBuilder straight = new StringBuilder();
			XWPFStyles styles = doc.getStyles();
			// 遍历段落
			while (itPara.hasNext()) {
				// 获取下一个段落
				XWPFParagraph paragraph = itPara.next();
				// 获取段落中的所有运行
				List<XWPFRun> runs = paragraph.getRuns();
				// 获取段落样式
				String styleID = paragraph.getStyleID();
				String name = "";
				String text = paragraph.getText();
				if (StrUtil.isNotBlank(styleID)) {
					XWPFStyle style1 = styles.getStyle(styleID);
					// name 格式太多 太杂
					name = style1.getName();

				}
				// 保存数据的开关
				boolean isTrue = true;
				if (StrUtil.isNotBlank(text)) {
					if (!name.contains("目录") && !text.contains("一、投标函") && text.contains("二、法定代表人身份证明")) {
						isTrue = false;
						break;
					}
					if (isTrue) {
						straight.append(text);
					}
				}

			}
			System.out.println("文章标题有：" + checkText(title.toString()));
			System.out.println("文章正文有：" + checkText(straight.toString()));
		}

	}

	/**
	 * word中的excel内容打印 docx/doc
	 * @param filePath 文件地址
	 * @throws IOException 异常
	 */
	public static void printWordExcelText(String filePath) throws IOException {

		// 加载文件
		File file = new File(filePath);
		// 将文件转成流
		InputStream in = Files.newInputStream(file.toPath());
		StringBuilder res = new StringBuilder();
		// 处理docx格式 即office2007以后版本
		if (filePath.endsWith(".docx")) {
			XWPFDocument xdoc = new XWPFDocument(in);
			List<XWPFTable> tables = xdoc.getTables();
			for (XWPFTable table : tables) {
				// 获取表格的行
				List<XWPFTableRow> rows = table.getRows();
				for (XWPFTableRow row : rows) {
					// 获取表格的每个单元格
					List<XWPFTableCell> tableCells = row.getTableCells();
					for (XWPFTableCell cell : tableCells) {
						// 获取单元格的内容
						String text1 = cell.getText();
						res.append(text1);
					}
				}
			}
		}
		else if (filePath.endsWith(".doc")) {
			// 处理doc格式 即office2003版本
			POIFSFileSystem pfs = new POIFSFileSystem(in);
			HWPFDocument hwpf = new HWPFDocument(pfs);
			Range range = hwpf.getRange();// 得到文档的读取范围
			TableIterator it = new TableIterator(range);
			// 迭代文档中的表格
			// 如果有多个表格只读取需要的一个 set是设置需要读取的第几个表格，total是文件中表格的总数
			int set = 1;
			int num = set;
			for (int i = 0; i < set - 1; i++) {
				it.hasNext();
				it.next();
			}
			while (it.hasNext()) {
				Table tb = it.next();
				System.out.println("这是第" + num + "个表的数据");
				num += 1;
				// 迭代行，默认从0开始,可以依据需要设置i的值,改变起始行数，也可设置读取到那行，只需修改循环的判断条件即可
				for (int i = 0; i < tb.numRows(); i++) {
					TableRow tr = tb.getRow(i);
					// 迭代列，默认从0开始
					for (int j = 0; j < tr.numCells(); j++) {
						TableCell td = tr.getCell(j);// 取得单元格
						// 取得单元格的内容
						for (int k = 0; k < td.numParagraphs(); k++) {
							Paragraph para = td.getParagraph(k);
							String s = para.text();
							// 去除后面的特殊符号
							if (null != s && !s.isEmpty()) {
								s = s.substring(0, s.length() - 1);
							}
							res.append(s).append("\t");
						}
					}
				}
			}
		}
		else {
			System.out.println("格式有误");
		}
		System.out.println(checkText(String.valueOf(res)));

	}

	/**
	 * 提取word中的图片-保存到指定文件 docx/doc 数量有误差不大
	 * @param filePath 文件路径
	 * @param imageFilePath 文件输出路径
	 */
	public static void getImageExtract(String filePath, String imageFilePath) {
		FileInputStream fileInputStream = null;
		try {
			fileInputStream = new FileInputStream(new File(filePath));
			if (filePath.endsWith(".docx")) {
				XWPFDocument document = new XWPFDocument(fileInputStream);
				List<XWPFPictureData> picList = document.getAllPictures();
				for (XWPFPictureData pic : picList) {
					byte[] bytev = pic.getData();
					FileOutputStream fos = new FileOutputStream(imageFilePath + "\\" + pic.getFileName());
					fos.write(bytev);
				}
				fileInputStream.close();
			}
			else if (filePath.endsWith(".doc")) {
				HWPFDocument doc = new HWPFDocument(fileInputStream);
				int length = doc.characterLength();
				PicturesTable pTable = doc.getPicturesTable();
				for (int i = 0; i < length; i++) {
					Range range = new Range(i, i + 1, doc);
					CharacterRun cr = range.getCharacterRun(0);
					if (pTable.hasPicture(cr)) {
						Picture pic = pTable.extractPicture(cr, false);

						String afileName = pic.suggestFullFileName();
						OutputStream out = new FileOutputStream(
								new File(imageFilePath + "\\" + System.currentTimeMillis() + afileName));
						pic.writeImageContent(out);
					}
				}
			}
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		finally {
			if (fileInputStream != null) {
				try {
					// 关闭资源
					fileInputStream.close();
				}
				catch (IOException e) {
					e.printStackTrace();
					System.out.println("读取Word文件失败...");
				}
			}
		}
	}

	/**
	 * doc/docx息加载段落信
	 */
	public static void analysisDocx(String filePath) throws IOException {

		// 加载文件
		File file = new File(filePath);
		// 将文件转成流
		InputStream inputStream = Files.newInputStream(file.toPath());
		if (filePath.endsWith(".docx")) {
			// 加载poi
			XWPFDocument xwpfDocument = new XWPFDocument(inputStream);
			// 创建poi解析器
			XWPFWordExtractor xwpfWordExtractor = new XWPFWordExtractor(xwpfDocument);
			// 从解析器中获取文件信息
			XWPFDocument extractorDocument = xwpfWordExtractor.getDocument();
			// 获取文件的总段落数
			List<XWPFParagraph> paragraphs = extractorDocument.getParagraphs();
			for (XWPFParagraph paragraph : paragraphs) {
				if (paragraph.isEmpty()) {
					continue;
				}
				if (StrUtil.isBlank(paragraph.getText())) {
					continue;
				}
				System.out.println("段落文字:" + paragraph.getText());
			}
			extractorDocument.close();
			xwpfWordExtractor.close();
			xwpfDocument.close();
		}
		else if (filePath.endsWith(".doc")) {

			HWPFDocument hwpfDocument = new HWPFDocument(inputStream);
			Range range = hwpfDocument.getRange();
			for (int i = 0; i < range.numParagraphs(); i++) {
				Paragraph paragraph = range.getParagraph(i);
				if (StrUtil.isNotBlank(paragraph.text())) {
					System.out.println("段落文字:" + paragraph.text());
				}
			}
		}
		inputStream.close();
	}

	/**
	 * 替换word中的文字
	 * @param filePath 替换文件所在路径
	 * @param map 替换数据集合 textMap.put("振翅高飞", "翱翔天际"); 左边换右边
	 */
	public static void wordTextSubstitution(String filePath, Map<String, String> map) {
		String textPath = "";
		File file = new File(filePath);
		String fileName = file.getName();
		try {
			if (filePath.endsWith(".docx")) {
				if (StrUtil.isNotBlank(fileName)) {
					String name = fileName.substring(0, fileName.length() - 5);
					textPath = filePath.replaceAll(fileName, name + "_" + System.currentTimeMillis() + ".docx");
				}
				XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(filePath));
				// 替换段落中的指定文字
				Iterator<XWPFParagraph> itPara = document.getParagraphsIterator();
				// 遍历段落
				while (itPara.hasNext()) {
					// 获取段落对象
					XWPFParagraph paragraph = itPara.next();
					// 获取段落中的文本块列表
					List<XWPFRun> runs = paragraph.getRuns();
					// 遍历文本块
					for (int i = 0; i < runs.size(); i++) {
						// 当前行的文本内容
						String oneparaString = runs.get(i).getText(runs.get(i).getTextPosition());
						// 如果当前行文本不为空
						if (oneparaString != null) {
							// ==========================
							// 获取当前文本块的字体和样式
							CTR cr = runs.get(i).getCTR();
							// 设置字体大小
							runs.get(i).setFontSize(22);
							int fontSize = runs.get(i).getFontSize();
							// ==========================
							// 遍历替换项
							for (Map.Entry<String, String> entry : map.entrySet()) {
								// 将文本中的待替换字符串替换为替换后的字符串
								oneparaString = oneparaString.replace(entry.getKey(), entry.getValue());
							}
							// 设置文本块的文本内容
							runs.get(i).setText(oneparaString, 0);
						}
					}
				}

				// 创建新文件存放新内容
				FileOutputStream outStream = new FileOutputStream(textPath);
				document.write(outStream);
				outStream.close();
				System.out.println("--- SUCCESS！");
			}
			else if (filePath.endsWith(".doc")) {
				if (StrUtil.isNotBlank(fileName)) {
					String name = fileName.substring(0, fileName.length() - 4);
					textPath = filePath.replaceAll(fileName, name + "_" + System.currentTimeMillis() + ".doc");
				}
				HWPFDocument document = new HWPFDocument(new FileInputStream(filePath));
				Range range = document.getRange();
				for (Map.Entry<String, String> entry : map.entrySet()) {
					range.replaceText(entry.getKey(), entry.getValue());
				}
				// 创建新文件存放新内容
				FileOutputStream outStream = new FileOutputStream(textPath);
				document.write(outStream);
				outStream.close();
				System.out.println("--- SUCCESS！");
			}
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}

	// =======================第二部分==================================

	/**
	 * 针对特殊字符进行解析，可以打印出来
	 * @param allText 文本
	 * @return 变异后的文本
	 */
	static String checkText(String allText) {
		// 获取所有文本
		allText = allText.replaceAll("\\t", " "); // 替换制表符为空格
		allText = allText.replaceAll("\\n", " "); // 替换换行符为换行
		allText = allText.replaceAll("\\r", " "); // 替换回车符为空字符
		allText = allText.replaceAll("\f", " "); // 替换特殊字符为空字符
		allText = allText.replaceAll("\u0001", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\ufffd", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\u0001", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\u0000", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\u0000", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\u0000", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\u0001", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\ufffd", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\u000E", ""); // 替换特殊字符为空字符
		allText = allText.replaceAll("\\p{Z}", " "); // 替换所有其他不可打印字符为空字符

		return allText;

	}

	/**
	 * 提取doc段落样式名称 标题、目录、正文等
	 */
	public static String getDocFontStyleName(HWPFDocument doc, Paragraph p) {
		// 获取段落样式索引
		int styleIndex = p.getStyleIndex();
		// 获取样式表
		StyleSheet styleSheet = doc.getStyleSheet();
		// 通过样式索引获取样式描述
		StyleDescription style = styleSheet.getStyleDescription(styleIndex);
		// 获取样式名称
		return style.getName();
	}

	/**
	 * 段落提取doc样式名称 标题、目录、正文等
	 */
	public static String getDocxFontStyleName(XWPFDocument docx, XWPFParagraph paragraph) {
		// 获取段落样式
		String styleID = paragraph.getStyleID();
		XWPFStyles styles = docx.getStyles();
		if (StrUtil.isNotBlank(styleID)) {
			XWPFStyle style1 = styles.getStyle(styleID);
			// name 格式太多 太杂
			return style1.getName();
		}

		return "";
	}

	/**
	 * 提取doc标题
	 */
	public static String getDocTitle(HWPFDocument doc, Paragraph p) {
		// 获取段落样式索引
		int styleIndex = p.getStyleIndex();
		// 获取样式表
		StyleSheet styleSheet = doc.getStyleSheet();
		// 通过样式索引获取样式描述
		StyleDescription style = styleSheet.getStyleDescription(styleIndex);
		// 获取样式名称
		String styleName = style.getName();

		if (StrUtil.isNotBlank(styleName) && StrUtil.isNotBlank(p.text())) {
			if (styleName.contains("标题")) {
				return p.text();
			}
		}
		return "";
	}

	/**
	 * 提取docx标题
	 */
	public static String getDocxTitle(XWPFDocument docx, XWPFParagraph paragraph) {
		// 获取段落样式
		String styleID = paragraph.getStyleID();
		String text = paragraph.getText();
		XWPFStyles styles = docx.getStyles();
		String name = "";
		if (StrUtil.isNotBlank(styleID)) {
			XWPFStyle style1 = styles.getStyle(styleID);
			// name 格式太多 太杂
			name = style1.getName();
		}
		if (StrUtil.isNotBlank(text)) {
			if (StrUtil.isNotBlank(name) && (name.contains("标题") || name.contains("heading"))) {
				return text;
			}
		}
		return "";
	}

	/**
	 * 提取doc文本
	 */
	public static String getDocText(HWPFDocument doc, Paragraph p) {
		// 获取段落样式索引
		int styleIndex = p.getStyleIndex();
		// 获取样式表
		StyleSheet styleSheet = doc.getStyleSheet();
		// 通过样式索引获取样式描述
		StyleDescription style = styleSheet.getStyleDescription(styleIndex);
		// 获取样式名称
		String styleName = style.getName();

		if (StrUtil.isNotBlank(styleName) && StrUtil.isNotBlank(p.text())) {
			if (styleName.contains("标题")) {
				return "";
			}
			else {
				return p.text();
			}
		}
		return "";
	}

	/**
	 * 提取docx文本
	 */
	public static String getDocxText(XWPFDocument docx, XWPFParagraph paragraph) {
		// 获取段落样式
		String styleID = paragraph.getStyleID();
		String text = paragraph.getText();
		XWPFStyles styles = docx.getStyles();
		String name = "";
		if (StrUtil.isNotBlank(styleID)) {
			XWPFStyle style1 = styles.getStyle(styleID);
			// name 格式太多 太杂
			name = style1.getName();
		}
		if (StrUtil.isNotBlank(text)) {
			if (StrUtil.isNotBlank(name) && (name.contains("标题") || name.contains("heading"))) {
				return "";
			}
			else {
				return text;
			}
		}
		return "";
	}

	/**
	 * 提取doc字体大小
	 */
	public static int getDocFontSize(Paragraph p) {
		CharacterRun characterRun = p.getCharacterRun(0);
		// 字体大小
		return characterRun.getFontSize();
	}

	/**
	 * 文本块提取docx字体大小 返回-1则为默认值
	 */
	public static int getDocxFontSize(XWPFRun run) {
		return run.getFontSize();
	}

	/**
	 * 提取doc字体是否加粗
	 */
	public static boolean getDocFontBold(Paragraph p) {
		CharacterRun characterRun = p.getCharacterRun(0);
		// 字体大小
		return characterRun.isBold();
	}

	/**
	 * 文本块提取docx字体是否加粗
	 */
	public static boolean getDocxFontBole(XWPFRun run) {
		return run.isBold();
	}

	/**
	 * 提取doc字体名称
	 */
	public static String getDocFontName(Paragraph p) {
		CharacterRun characterRun = p.getCharacterRun(0);
		// 字体大小
		return characterRun.getFontName();
	}

	/**
	 * 提取docx字体名称
	 */
	public static String getDocxFontName(XWPFRun run) {
		return run.getFontName();
	}

	/**
	 * word中的excel内容打印 doc
	 */
	public static String getDocExcel(HWPFDocument doc) {

		StringBuilder res = new StringBuilder();
		Range range = doc.getRange();// 得到文档的读取范围
		TableIterator it = new TableIterator(range);
		// 迭代文档中的表格
		// 如果有多个表格只读取需要的一个 set是设置需要读取的第几个表格，total是文件中表格的总数
		int set = 1;
		int num = set;
		for (int i = 0; i < set - 1; i++) {
			it.hasNext();
			it.next();
		}
		while (it.hasNext()) {
			Table tb = it.next();
			num += 1;
			// 迭代行，默认从0开始,可以依据需要设置i的值,改变起始行数，也可设置读取到那行，只需修改循环的判断条件即可
			for (int i = 0; i < tb.numRows(); i++) {
				TableRow tr = tb.getRow(i);
				// 迭代列，默认从0开始
				for (int j = 0; j < tr.numCells(); j++) {
					TableCell td = tr.getCell(j);// 取得单元格
					// 取得单元格的内容
					for (int k = 0; k < td.numParagraphs(); k++) {
						Paragraph para = td.getParagraph(k);
						String s = para.text();
						// 去除后面的特殊符号
						if (null != s && !"".equals(s)) {
							s = s.substring(0, s.length() - 1);
						}
						res.append(s).append("\t");
					}
				}
			}
		}
		return checkText(String.valueOf(res));

	}

	/**
	 * word中的excel内容打印 docx
	 */
	public static String getDocxExcel(XWPFDocument xdoc) {
		StringBuilder res = new StringBuilder();
		List<XWPFTable> tables = xdoc.getTables();
		for (XWPFTable table : tables) {
			// 获取表格的行
			List<XWPFTableRow> rows = table.getRows();
			for (XWPFTableRow row : rows) {
				// 获取表格的每个单元格
				List<XWPFTableCell> tableCells = row.getTableCells();
				for (XWPFTableCell cell : tableCells) {
					// 获取单元格的内容
					String text1 = cell.getText();
					res.append(text1);
				}
			}
		}
		return checkText(String.valueOf(res));

	}

	/**
	 * DOC按照区间层次打印文件的文本 指定开始中文及结束中文及样式
	 * @param doc 文档
	 * @param p 段落
	 * @param styleName 不包含的样式名称-目录、标题、正文等 !styleName.contains("目录")
	 * @param startText 开始文字内容
	 * @param endText 结束文字内容
	 * @return 是否继续打印-不满足条件返回false 则直接break停止循环
	 */
	public static boolean getDocTextBySection(HWPFDocument doc, Paragraph p, String styleName, String startText,
			String endText) {

		// 保存数据的开关
		boolean isTrue = true;
		// 获取段落样式索引
		int styleIndex = p.getStyleIndex();
		// 获取样式表
		StyleSheet styleSheet = doc.getStyleSheet();
		// 通过样式索引获取样式描述
		StyleDescription style = styleSheet.getStyleDescription(styleIndex);
		// 获取样式名称
		String styleName1 = style.getName();
		if (StrUtil.isNotBlank(p.text())) {
			if (!styleName1.contains(styleName) && !p.text().contains(startText) && p.text().contains(endText)) {
				isTrue = false;
			}
		}
		return isTrue;

	}

	/**
	 * DOCX按照区间层次打印文件的文本 指定开始中文及结束中文及样式
	 * @param doc 文档
	 * @param paragraph 段落
	 * @param styleName 不包含的样式名称-目录、标题、正文等 !name.contains("目录")
	 * @param startText 开始文字内容
	 * @param endText 结束文字内容
	 * @return 是否继续打印-不满足条件返回false 则直接break停止循环
	 */
	public static boolean getDocxTextBySection(XWPFDocument doc, XWPFParagraph paragraph, String styleName,
			String startText, String endText) throws IOException {

		// 获取段落样式
		String styleID = paragraph.getStyleID();
		String name = "";
		String text = paragraph.getText();
		XWPFStyles styles = doc.getStyles();
		if (StrUtil.isNotBlank(styleID)) {
			XWPFStyle style1 = styles.getStyle(styleID);
			// name 格式太多 太杂
			name = style1.getName();
		}
		// 保存数据的开关
		boolean isTrue = true;
		if (StrUtil.isNotBlank(text)) {
			if (!name.contains(styleName) && !text.contains(startText) && text.contains(endText)) {
				isTrue = false;
			}
		}
		return isTrue;

	}

	/**
	 * 提取doc中的图片-保存到指定文件
	 * @param doc 文档
	 * @param imageFilePath 文件输出路径
	 */
	public static void getDocImage(HWPFDocument doc, String imageFilePath) throws IOException {
		int length = doc.characterLength();
		PicturesTable pTable = doc.getPicturesTable();
		for (int i = 0; i < length; i++) {
			Range range = new Range(i, i + 1, doc);
			CharacterRun cr = range.getCharacterRun(0);
			if (pTable.hasPicture(cr)) {
				Picture pic = pTable.extractPicture(cr, false);
				String afileName = pic.suggestFullFileName();
				OutputStream out = new FileOutputStream(imageFilePath + "\\" + System.currentTimeMillis() + afileName);
				pic.writeImageContent(out);
			}
		}

	}

	/**
	 * 提取docx中的图片-保存到指定文件
	 * @param document 文档
	 * @param imageFilePath 文件输出路径
	 */
	public static void getDocxImage(XWPFDocument document, String imageFilePath) throws IOException {
		List<XWPFPictureData> picList = document.getAllPictures();
		for (XWPFPictureData pic : picList) {
			byte[] bytev = pic.getData();
			FileOutputStream fos = new FileOutputStream(imageFilePath + "\\" + pic.getFileName());
			fos.write(bytev);
		}
	}

	// =========================新增方法==========================================================

	/**
	 * 添加一个段落-docx
	 * @param docxDocument 文档
	 * @param alignment 对齐方式
	 * @param firstLineIndent 首行缩进
	 * @param textList 段落内容
	 * @param isBold 是否加粗
	 * @param fontFamily 宋体
	 * @param fontSize 文本大小
	 * @param kerning 和下一段距离
	 */
	public void setDocxParagraph(XWPFDocument docxDocument, ParagraphAlignment alignment, int firstLineIndent,
			List<String> textList, boolean isBold, String fontFamily, int fontSize, int kerning) {

		// 创建第一段落
		XWPFParagraph paragraphX = docxDocument.createParagraph();
		// paragraphX.setAlignment(ParagraphAlignment.CENTER);//对齐方式
		paragraphX.setAlignment(alignment);// 对齐方式
		paragraphX.setFirstLineIndent(firstLineIndent);// 首行缩进

		XWPFRun runTitle = paragraphX.createRun();
		textList.forEach(runTitle::setText);
		runTitle.setBold(isBold);
		runTitle.setFontSize(fontSize);
		runTitle.setFontFamily(fontFamily);
		runTitle.addCarriageReturn();// 回车键
		runTitle.setKerning(kerning);// 和下一段距离
	}

	/**
	 * docx设置excel
	 * @param docxDocument doc
	 * @param rows 行
	 * @param cols 列
	 * @param width 宽度百分比 95%
	 */
	public void setDocxExcel(XWPFDocument docxDocument, int rows, int cols, String width) throws IOException {

		// 创建表格 4行*5列(创建table 时，会有一个默认一行一列的表格)
		// XWPFTable table = createTable( docxDocument,4,5);
		XWPFTable table = docxDocument.createTable(rows, cols);
		table.setWidth(width);
		table.setWidthType(TableWidthType.PCT);// 设置表格相对宽度
		table.setTableAlignment(TableRowAlign.CENTER);// 居中

		// 合并单元格
		// newHMerge(table,0,0);
		// //给表格填充文本
		// setDocxTableText(docxDocument);
	}

	/**
	 * 给excel填充文本
	 * @param docxDocument doc
	 */
	private void setDocxTableText(XWPFDocument docxDocument) throws IOException {
		// 获取第一个表格
		XWPFTable table = docxDocument.getTableArray(0);
		List<XWPFTableRow> rows = table.getRows();
		int i = 1;
		for (XWPFTableRow row : rows) {
			List<XWPFTableCell> cells = row.getTableCells();
			for (XWPFTableCell cell : cells) {

				// 填充内容
				cell.setText("第" + String.valueOf(i++) + "格");
				cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
				// cell.setWidthType(TableWidthType.PCT);
				// cell.setWidth("30%");
			}
		}
		// exportDocxWord(docxDocument,"D:\\file\\生成地址");
	}

	/**
	 * 合并单元格
	 * @param table table
	 * @param rows 表格行对象
	 * @param cols 列
	 */
	public void newHMerge(XWPFTable table, int rows, int cols) {
		// 合并单元格位置-几行几列
		XWPFTableRow row1 = table.getRow(rows); // 获取第rows行的表格行对象
		XWPFTableCell cell1 = row1.getCell(cols); // 获取第cols列的表格单元格对象
		CTTcPr cellCtPr = getDocxCellCTTcPr(cell1); // 获取单元格的CTTcPr对象
		cellCtPr.addNewHMerge().setVal(STMerge.RESTART); // 设置单元格水平合并属性为开始合并
	}

	/**
	 * 合并单元格
	 * @param cell 列
	 * @return
	 */
	public static CTTcPr getDocxCellCTTcPr(XWPFTableCell cell) {
		CTTc cttc = cell.getCTTc();
		CTTcPr tcPr = cttc.isSetTcPr() ? cttc.getTcPr() : cttc.addNewTcPr();
		return tcPr;
	}

	/**
	 * 导出word文档 -- 不能用、文档打不开
	 * @param docDocument 定义好的文档
	 * @param path 生成地址,文件名称由此生成
	 */
	public static void exportDocWord(HWPFDocument docDocument, String path) throws IOException {

		if (StrUtil.isNotBlank(path)) {
			path = path + "\\" + System.currentTimeMillis() + ".doc";
		}

		FileOutputStream stream = new FileOutputStream(path);
		docDocument.write(stream);
		stream.close();
	}

	/**
	 * 导出word文档
	 * @param docxDocument 定义好的文档
	 * @param path 生成地址,文件名称由此生成
	 */
	public static void exportDocxWord(XWPFDocument docxDocument, String path) throws IOException {

		if (StrUtil.isNotBlank(path)) {
			path = path + "\\" + System.currentTimeMillis() + ".docx";
		}

		FileOutputStream stream = new FileOutputStream(path);
		docxDocument.write(stream);
		stream.close();
	}

	/**
	 * 替换文本内容
	 * @param paragraph 段落内容
	 * @param textMap 需要替换的内容：旧内容：新内容
	 */
	public static void replaceDocxText(XWPFParagraph paragraph, Map<String, String> textMap) {
		// 获取段落中的文本块列表
		List<XWPFRun> runs = paragraph.getRuns();
		// 遍历文本块
		for (int i = 0; i < runs.size(); i++) {
			// 当前文本块内容
			String oneparaString = runs.get(i).getText(runs.get(i).getTextPosition());
			if (oneparaString != null) {
				// 遍历文本映射
				for (Map.Entry<String, String> entry : textMap.entrySet()) {
					// 如果段落文本包含键值
					if (paragraph.getText().contains(entry.getKey())) {
						// 替换文本块中的键值为映射中的值
						oneparaString = oneparaString.replace(entry.getKey(), entry.getValue());
						// 字体大小、颜色
						// replaceDocxTextFont(runs.get(i),"30","ED0909","");
					}
				}
				// 更新文本块的文本内容
				runs.get(i).setText(oneparaString, 0);
			}
		}
	}

	/**
	 * 设置字体样式
	 * @param run 文本块内容
	 * @param fontSize 大小
	 * @param color 颜色
	 * @param fontFamily 字体
	 */
	public static void replaceDocxTextFont(XWPFRun run, String fontSize, String color, String fontFamily) {
		// 设置当前文本块颜色及修改大小
		if (StrUtil.isNotBlank(fontSize)) {
			run.setFontSize(Integer.parseInt(fontSize));
		}
		if (StrUtil.isNotBlank(color)) {
			run.setColor(color);
		}
	}

	/**
	 * 删除包含下划线的文本块内容-- TODO 注意：不好用，文本块内容范围比较大
	 * @param paragraph p
	 */
	public static void delDocxIsUnderlinedText(XWPFParagraph paragraph) {
		// 遍历文本块
		List<XWPFRun> runs = paragraph.getRuns();
		for (int i = 0; i < runs.size(); i++) {
			XWPFRun run = runs.get(i);
			// 判断是否有下划线
			UnderlinePatterns underline = run.getUnderline();
			int value = underline.getValue();
			// 清空上一个文本块的内容
			if (value > 0 && i > 0) {
				XWPFRun previousRun = runs.get(i - 1);
				previousRun.setText("", 0);
			}
		}

	}

	/**
	 * 修改文本样式
	 * @param paragraph 段落
	 * @param textMap <需要修改的内容：<样式名称：样式>>
	 */
	public static void updDocFontStyle(Paragraph paragraph, Map<String, Map<String, String>> textMap) {

		// 遍历文本映射
		for (Map.Entry<String, Map<String, String>> entry : textMap.entrySet()) {
			if (paragraph.text().equals(entry.getKey())) {
				int i = Integer.parseInt(entry.getValue().get(FontStyleEnum.STYLE_ID.getValue()));
				paragraph.getProps().setLvl((byte) i);
			}
		}
	}

	/**
	 * 修改文本样式
	 * @param paragraph 段落
	 * @param textMap <需要修改的内容：<样式名称：样式>>
	 */
	public static void updDocxFontStyle(XWPFParagraph paragraph, Map<String, Map<String, String>> textMap) {

		// 遍历文本映射
		for (Map.Entry<String, Map<String, String>> entry : textMap.entrySet()) {
			if (paragraph.getText().equals(entry.getKey())) {
				paragraph.setStyle(entry.getValue().get(FontStyleEnum.STYLE_ID.getValue()));
			}
		}
	}

	/**
	 * 替换文本颜色及字体大小---功能有点问题 TODO 当前方法只适合修改内容的颜色及字体大小
	 * @param paragraph 段落内容
	 * @param textMap 需要替换的内容：旧内容：新内容
	 */
	public static void replaceDocxText111(XWPFParagraph paragraph, Map<String, Map<String, String>> textMap) {

		// 获取段落中的文本块列表
		List<XWPFRun> runs = paragraph.getRuns();
		// 遍历文本块
		for (int i = 0; i < runs.size(); i++) {
			// 当前文本块内容
			String oneparaString = runs.get(i).getText(runs.get(i).getTextPosition());
			if (oneparaString != null) {
				// 遍历文本映射
				for (Map.Entry<String, Map<String, String>> entry : textMap.entrySet()) {
					// 如果段落文本包含键值
					if (oneparaString.equals(entry.getKey())) {
						Map<String, String> value = entry.getValue();
						// 字体大小、颜色、字体
						replaceDocxTextFont(runs.get(i), value.get("fontSize"), value.get("color"),
								value.get("fontFamily"));
						paragraph.setStyle(value.get("styleId"));

					}
				}
				// 更新文本块的文本内容
				runs.get(i).setText(oneparaString, 0);
			}
		}
	}

	/**
	 * 读取模板样式-获取模板的内容及样式
	 * @param filePath 模板路径
	 * @return Map<段落内容 ,Map<样式key,样式value>>
	 */
	public static Map<String, Map<String, String>> getDocxTemplate(String filePath) throws IOException {
		InputStream fileInputStream = new FileInputStream(filePath);
		// 创建一个新的XWPFDocument对象，从输入流is中读取内容
		XWPFDocument docx = new XWPFDocument(fileInputStream);
		// 获取段落迭代器
		Iterator<XWPFParagraph> itParax = docx.getParagraphsIterator();
		Map<String, Map<String, String>> map = new HashMap<>();
		// 遍历段落
		while (itParax.hasNext()) {
			XWPFParagraph next = itParax.next();
			// 解析模板内容，存储内容对应的样式
			if (StrUtil.isNotBlank(next.getText())) {
				Map<String, String> hashMap = new HashMap<>();
				hashMap.put(FontStyleEnum.STYLE_ID.getValue(), next.getStyleID());
				map.put(next.getText(), hashMap);
			}
		}
		fileInputStream.close();
		docx.close();
		return map;
	}

	/**
	 * 读取模板样式-获取模板的内容及样式
	 * @param filePath 模板路径
	 * @return Map<段落内容 ,Map<样式key,样式value>>
	 */
	public static Map<String, Map<String, String>> getDocTemplate(String filePath) throws IOException {
		InputStream fileInputStream = new FileInputStream(filePath);
		// 创建一个新的HWPFDocument对象，从输入流is中读取内容
		HWPFDocument doc = new HWPFDocument(fileInputStream);
		// 获取段落迭代器
		Range r = doc.getRange();
		Map<String, Map<String, String>> map = new HashMap<>();
		// 遍历段落
		for (int i = 0; i < r.numParagraphs(); i++) {
			// 获取第 i 个段落
			Paragraph paragraph = r.getParagraph(i);
			// 解析模板内容，存储内容对应的样式
			if (StrUtil.isNotBlank(paragraph.text())) {
				Map<String, String> hashMap = new HashMap<>();
				hashMap.put(FontStyleEnum.STYLE_ID.getValue(), String.valueOf(paragraph.getLvl()));
				map.put(paragraph.text(), hashMap);
			}
		}
		fileInputStream.close();
		doc.close();
		return map;
	}

	/**
	 * docx设置页边距 取值规则: 规则是磅数 * 20 例设置上、下、左、右的页边距为1CM，1 * 28.5 * 20 = 567
	 * @param document doc对象
	 * @param left 左边距 567
	 * @param right 右边距 567
	 * @param top 上边距 567
	 * @param bottom 下边距 567
	 */
	public static void setDocxPageMargin(XWPFDocument document, long left, long right, long top, long bottom) {
		CTSectPr ctSectPr = document.getDocument().getBody().addNewSectPr();
		CTPageMar pageMar = ctSectPr.addNewPgMar();
		pageMar.setLeft(BigInteger.valueOf(left));
		pageMar.setRight(BigInteger.valueOf(right));
		pageMar.setTop(BigInteger.valueOf(top));
		pageMar.setBottom(BigInteger.valueOf(bottom));
	}

	/**
	 * docx设置纸张大小 width与height的取值规则: 规则是纸张的长度（磅数）* 20 例横向A4纸的大小是 29.7（厘米）*
	 * 21（厘米），首先厘米转磅数在乘以20 宽：29.7 * 28.35 * 20 = 16839.9 ≈ 16840 长：21 * 28.35 * 20 = 11907
	 * @param document doc对象
	 * @param width 宽
	 * @param height 长
	 */
	public static void setDocxPageSize(XWPFDocument document, long width, long height) {
		CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
		CTPageSz pgsz = sectPr.isSetPgSz() ? sectPr.getPgSz() : sectPr.addNewPgSz();
		pgsz.setW(BigInteger.valueOf(width));
		pgsz.setH(BigInteger.valueOf(height));
	}

	// =======================第三部分==================================

	/**
	 * 设置目录（当前允许 三个级别的目录 ） 使用规则： 返回值 List<String> 为各级标题的样式值，以下标取用，如：设置一级标题，取集合index=0的值
	 */
	public static List<String> addToc(XWPFDocument document) {
		List<XWPFParagraph> paragraphs = document.getParagraphs();
		XWPFParagraph catalogP = null;
		if (!paragraphs.isEmpty() && paragraphs.get(paragraphs.size() - 1).isPageBreak()) {
			catalogP = paragraphs.get(paragraphs.size() - 1);
		}
		else {
			catalogP = document.createParagraph();
		}
		setStyle(catalogP.createRun(), "微软雅黑", 16, true, "目   录", "");
		catalogP.setAlignment(ParagraphAlignment.CENTER);

		XWPFParagraph paragraph = document.createParagraph();
		CTSimpleField ctSimpleField = paragraph.getCTP().addNewFldSimple();
		ctSimpleField.setInstr("TOC \\o \"1-4\" \\h \\z \\u");
		ctSimpleField.setDirty(true);
		ctSimpleField.addNewR().addNewT().setStringValue("<<请");
		CTR ctr = ctSimpleField.addNewR();
		ctr.addNewT().setStringValue("右击");
		ctr.addNewRPr().addNewColor().setVal("FF0000");

		ctSimpleField.addNewR().addNewT().setStringValue("该文本，在弹出框中选择【");

		CTR ctr2 = ctSimpleField.addNewR();
		ctr2.addNewT().setStringValue("更新域");
		ctr2.addNewRPr().addNewColor().setVal("FF0000");

		ctSimpleField.addNewR().addNewT().setStringValue("】 ，方可为您生成目录>>");

		String head1 = "1"; // 一级标题
		String head2 = "2"; // 二级标题
		String head3 = "3"; // 三级标题
		addCustomHeadingStyle(document, head1, 1);
		addCustomHeadingStyle(document, head2, 2);
		addCustomHeadingStyle(document, head3, 3);

		List<String> tocHeades = new ArrayList<>();
		tocHeades.add(head1);
		tocHeades.add(head2);
		tocHeades.add(head3);
		setPageBreak(document);
		return tocHeades;
	}

	/**
	 * 增加自定义标题样式。这里用的是stackoverflow的源码
	 * @param docxDocument 目标文档
	 * @param strStyleId 样式名称
	 * @param headingLevel 样式级别
	 */
	private static void addCustomHeadingStyle(XWPFDocument docxDocument, String strStyleId, int headingLevel) {

		CTStyle ctStyle = CTStyle.Factory.newInstance();
		ctStyle.setStyleId(strStyleId);

		CTString styleName = CTString.Factory.newInstance();
		styleName.setVal(strStyleId);
		ctStyle.setName(styleName);

		CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
		indentNumber.setVal(BigInteger.valueOf(headingLevel));

		// lower number > style is more prominent in the formats bar
		ctStyle.setUiPriority(indentNumber);

		CTOnOff onoffnull = CTOnOff.Factory.newInstance();
		ctStyle.setUnhideWhenUsed(onoffnull);

		// style shows up in the formats bar
		ctStyle.setQFormat(onoffnull);

		// style defines a heading of the given level
		CTPPrGeneral ctpPrGeneral = CTPPrGeneral.Factory.newInstance();
		ctpPrGeneral.setOutlineLvl(indentNumber);
		ctStyle.setPPr(ctpPrGeneral);

		XWPFStyle style = new XWPFStyle(ctStyle);

		// is a null op if already defined
		XWPFStyles styles = docxDocument.createStyles();

		style.setType(STStyleType.PARAGRAPH);
		styles.addStyle(style);

	}

	/**
	 * 设置段落标题样式，用来生成目录
	 * @param headStyle 即通过 addToc 方法获取到的目录样式集合中的一个
	 */
	public static void setParagraphStyle(XWPFParagraph paragraph, String headStyle) {
		paragraph.setStyle(headStyle);
	}

	/**
	 * 添加图片【内嵌图片】
	 * @param width 单位 磅
	 * @param height 单位 磅
	 * @Return
	 */
	public static void addPicture_Inline(XWPFRun run, InputStream pictureData, int pictureType, String filename,
			float width, float height) throws Exception {
		run.addPicture(pictureData, pictureType, filename, ptToEMUs(width), ptToEMUs(height));
	}

	/**
	 * 添加图片【内嵌图片】
	 * @Auther lu zhifeng
	 * @Date 2020/11/14 12:27 上午
	 * @param width 单位 磅
	 * @param height 单位 磅
	 * @param backGColor 图片染色 如：ffffff
	 * @Return
	 */
	public static void addPicture_Inline(XWPFRun run, InputStream pictureData, int pictureType, String filename,
			float width, float height, String backGColor) throws Exception {
		run.addPicture(pictureData, pictureType, filename, ptToEMUs(width), ptToEMUs(height));
		CTDrawing drawing = run.getCTR().getDrawingArray(0);
		CTInline inline = drawing.getInlineArray(0);

		// 给图片添加颜色
		if (!StringUtil.isEmpty(backGColor)) {
			String xmlColor = "<a:duotone>\n" + "<a:prstClr val=\"black\"/>\n" + "<a:srgbClr val=\"" + backGColor
					+ "\">\n" +
					// "<a:tint val=\"45000\"/>\n" +
					// "<a:satMod val=\"400000\"/>\n" +
					"</a:srgbClr>\n" + "</a:duotone>\n" + "</a:blip>";

			String string = inline.getGraphic().getGraphicData().xmlText();

			String toReplaceStr = string.replaceAll(".*(<a:blip.*?/>).*", "$1");
			String targetReplace = toReplaceStr.replaceAll("/>", ">") + xmlColor;
			String result = string.replaceAll(toReplaceStr, targetReplace);

			XmlObject parse = XmlObject.Factory.parse(result);
			inline.getGraphic().getGraphicData().set(parse);
		}
	}

	// /**
	// * 添加图片[环绕模式：文字上方/下方]
	// * @param pictureData 图片数据
	// * @param pictureType 图片类型，参考 ({@link XWPFDocument#PICTURE_TYPE_PNG})
	// * @param filename 图片名
	// * @param width 图片宽 单位 磅
	// * @param height 图片高 单位 磅
	// * @param wrapType 环绕模式：{@link WordBaseConstants#AROUND_BEHIND_OVER}
	// * @param positionH 水平方向的移动数值 单位 磅
	// * @param relFromH 水平方向的位置相对于谁进行调整
	// * @param positionV 垂直方向的移动数值 单位 磅
	// * @param relFromV 垂直方向的位置相对于谁进行调整
	// * @param backGColor 图片染色
	// */
	// public static void addPicture_Anchor(XWPFRun run , InputStream pictureData, int
	// pictureType, String filename,
	// float width, float height , float positionH , STRelFromH.Enum relFromH,
	// float positionV , STRelFromV.Enum relFromV , int wrapType , String backGColor)
	// throws IOException {
	// try {
	// run.addPicture(pictureData , pictureType , filename , ptToEMUs(width) ,
	// ptToEMUs(height));
	// //获取刚才添加的图片数据
	// CTDrawing drawing = run.getCTR().getDrawingArray(0);
	// CTGraphicalObject graphicalobject = drawing.getInlineArray(0).getGraphic();
	//
	// long id = drawing.getInlineArray(0).getDocPr().getId(); //使用原来的id
	//
	// //设置一个浮动模式，替换掉默认的行内模式
	// CTAnchor ctAnchor = drawing.addNewAnchor();
	// String xml = "<a:graphic xmlns:a=\"" +
	// CTGraphicalObject.type.getName().getNamespaceURI() + "\">" + "<a:graphicData
	// uri=\"" +
	// org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture.type.getName().getNamespaceURI()
	// + "\">" + "<pic:pic xmlns:pic=\"" +
	// org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture.type.getName().getNamespaceURI()
	// + "\" />" + "</a:graphicData>" + "</a:graphic>";
	// InputSource is = new InputSource(new StringReader(xml));
	// org.w3c.dom.Document doc = DocumentHelper.readDocument(is);
	// ctAnchor.set(org.apache.xmlbeans.XmlToken.Factory.parse(doc.getDocumentElement(),
	// POIXMLTypeLoader.DEFAULT_XML_OPTIONS));
	// ctAnchor.setDistT(0L); //位置
	// ctAnchor.setDistR(0L);
	// ctAnchor.setDistB(0L);
	// ctAnchor.setDistL(0L);
	// ctAnchor.setLocked(false);
	// ctAnchor.setLayoutInCell(true);
	// ctAnchor.setSimplePos2(false);
	// ctAnchor.setAllowOverlap(true);
	// ctAnchor.setRelativeHeight(0);
	//
	// if(wrapType == WordBaseConstants.AROUND_BEHIND_OVER){
	// ctAnchor.setBehindDoc(false);
	// ctAnchor.addNewWrapNone(); //浮动模式，无文字环绕
	// }else if(wrapType == WordBaseConstants.AROUND_BEHIND_UNDER){
	// ctAnchor.setBehindDoc(true);
	// ctAnchor.addNewWrapNone(); //浮动模式，无文字环绕
	// }else if(wrapType == WordBaseConstants.AROUND_SQUARE){
	// ctAnchor.addNewWrapSquare();
	// }else if(wrapType == WordBaseConstants.AROUND_TIGHT){
	// ctAnchor.addNewWrapTight();
	// }else if(wrapType == WordBaseConstants.AROUND_THROUGH){
	// ctAnchor.addNewWrapThrough();
	// }else if(wrapType == WordBaseConstants.AROUND_TOP_BOTTOM){
	// ctAnchor.addNewWrapTopAndBottom();
	// }
	// CTPosH ctPosH = ctAnchor.addNewPositionH();
	// ctPosH.setRelativeFrom(relFromH == null ? STRelFromH.CHARACTER : relFromH);
	// ctPosH.setPosOffset(ptToEMUs(positionH));
	// CTPosV ctPosV = ctAnchor.addNewPositionV();
	// ctPosV.setRelativeFrom(relFromV == null ? STRelFromV.PARAGRAPH : relFromV);
	// ctPosV.setPosOffset(ptToEMUs(positionV));
	//
	// CTPoint2D ctPoint2D = ctAnchor.addNewSimplePos();
	// ctPoint2D.setX(0);
	// ctPoint2D.setY(0);
	// ctAnchor.addNewCNvGraphicFramePr();
	// CTNonVisualDrawingProps docPr = ctAnchor.addNewDocPr();
	// docPr.setId(id);
	// docPr.setName("Drawing " + id);
	// docPr.setDescr(filename);
	//
	// CTPositiveSize2D extent = ctAnchor.addNewExtent();
	// extent.setCx(ptToEMUs(width));
	// extent.setCy(ptToEMUs(height));
	//
	// ctAnchor.setGraphic(graphicalobject);
	//
	// drawing.setAnchorArray(new CTAnchor[]{ctAnchor});//添加浮动属性
	// drawing.removeInline(0);//删除行内属性
	//
	// //给图片添加颜色
	// if(!StringUtil.isEmpty(backGColor)){
	// String xmlColor = "<a:duotone>\n" +
	// "<a:prstClr val=\"black\"/>\n" +
	// "<a:srgbClr val=\"" + backGColor + "\">\n" +
	//// "<a:tint val=\"45000\"/>\n" +
	//// "<a:satMod val=\"400000\"/>\n" +
	// "</a:srgbClr>\n" +
	// "</a:duotone>\n" +
	// "</a:blip>";
	//
	// String string = ctAnchor.getGraphic().getGraphicData().xmlText();
	//
	// String toReplaceStr = string.replaceAll(".*(<a:blip.*?/>).*", "$1");
	// String targetReplace = toReplaceStr.replaceAll("/>", ">") + xmlColor;
	// String result = string.replaceAll(toReplaceStr, targetReplace);
	//
	// XmlObject parse = XmlObject.Factory.parse(result);
	// ctAnchor.getGraphic().getGraphicData().set(parse);
	// }
	//
	// } catch (XmlException | SAXException | InvalidFormatException var31) {
	// throw new IllegalStateException(var31);
	// }
	// }

	// 当前方法未验证是否影响功能
	private static int ptToEMUs(float width) {
		return Math.round(width);
	}

	/**
	 * 设置纸张大小 若要强限制纸张大小的参数，可以将常量改为枚举
	 * @param wordConstants_paperSize {@link WordBaseConstants#PAPER_SIZE_A4}
	 */
	public static void setPaperSize(XWPFDocument document, String wordConstants_paperSize) {
		String[] split = wordConstants_paperSize.split("-");
		CTBody body = document.getDocument().getBody();
		CTSectPr sectPr = body.isSetSectPr() ? body.getSectPr() : body.addNewSectPr();
		CTPageSz pageSize = sectPr.addNewPgSz();
		int w = Integer.parseInt(split[0]) * 20;
		int h = Integer.parseInt(split[1]) * 20;
		pageSize.setW(BigInteger.valueOf(w));
		pageSize.setH(BigInteger.valueOf(h));
		if (w > h) { // 横版
			pageSize.setOrient(STPageOrientation.LANDSCAPE);
		}
		else {
			pageSize.setOrient(STPageOrientation.PORTRAIT);
		}
	}

	/**
	 * 获取word纸张高度 单位 磅 【需提前设置过纸张大小，获取不到返回0】
	 */
	public static int getPaperSizeH(XWPFDocument docxDocument) {
		try {
			return (Integer) docxDocument.getDocument().getBody().getSectPr().getPgSz().getH() / 20;
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}

	/**
	 * 获取word纸张宽度 单位 磅 【需提前设置过纸张大小，获取不到返回0】
	 */
	public static int getPaperSizeW(XWPFDocument docxDocument) {
		try {
			return (Integer) docxDocument.getDocument().getBody().getSectPr().getPgSz().getW() / 20;
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}

	/**
	 * 设置页边距 单位 磅
	 */
	public static void setPageMargin(XWPFDocument document, int left, int top, int right, int bottom) {
		// 一磅 = 20缇
		CTBody body = document.getDocument().getBody();
		CTSectPr sectPr = body.isSetSectPr() ? body.getSectPr() : body.addNewSectPr();
		CTPageMar ctpagemar = sectPr.isSetPgMar() ? sectPr.getPgMar() : sectPr.addNewPgMar();
		ctpagemar.setLeft(new BigInteger((left * 20) + ""));
		ctpagemar.setTop(new BigInteger((top * 20) + ""));
		ctpagemar.setRight(new BigInteger((right * 20) + ""));
		ctpagemar.setBottom(new BigInteger((bottom * 20) + ""));
	}

	/** A4纸 常规 页边距 */
	public static void setPageMargin_norm(XWPFDocument document) {
		setPageMargin(document, 72, 90, 72, 90);
	}

	/**
	 * 获取页面左边距(正文左边距到纸张左边距) 单位 磅
	 */
	public static int getPageLeftMergin(XWPFDocument document) {
		CTPageMar ctPageMar = getCTPageMar(document);
		return (Integer) ctPageMar.getLeft() / 20;
	}

	/**
	 * 获取页面上边距（正文上边距到纸张上边距的距离） 单位 磅
	 */
	public static int getPageTopMergin(XWPFDocument document) {
		CTPageMar ctPageMar = getCTPageMar(document);
		return (Integer) ctPageMar.getTop() / 20;
	}

	/**
	 * 获取页面右边距（正文右边距到纸张右边距的距离） 单位 磅
	 */
	public static int getPageRightMergin(XWPFDocument document) {
		CTPageMar ctPageMar = getCTPageMar(document);
		return (Integer) ctPageMar.getRight() / 20;
	}

	/**
	 * 获取页面下边距（正文下边距到纸张下边距的距离） 单位 磅
	 */
	public static int getPageBottomMergin(XWPFDocument document) {
		CTPageMar ctPageMar = getCTPageMar(document);
		return (Integer) ctPageMar.getBottom() / 20;
	}

	/**
	 * 获取word正文宽度 单位 磅 【获取不到返回0，须提前设置纸张大小，页边距，若不设置，word处理的时候会使用默认的】
	 */
	public static int getPagebodyWidth(XWPFDocument document) {
		try {
			// 下面这个单位（缇）是英寸 * 1440 得到的， 一英寸 = 914400 EMUs
			CTSectPr sectPr = document.getDocument().getBody().getSectPr();
			if (sectPr != null) {
				int pageSize = ((BigInteger) sectPr.getPgSz().getW()).intValue(); // 纸张大小
				int leftMar = ((BigInteger) sectPr.getPgMar().getLeft()).intValue(); // 左缩进
				int rightMar = ((BigInteger) sectPr.getPgMar().getRight()).intValue(); // 右缩进
				return (pageSize - leftMar - rightMar) / 20;
			}
			else {
				return WordBaseConstants.PAPER_A4_W - 72 - 72; // 不设置纸张大小和边距的情况下默认宽度
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}

	/**
	 * 获取word正文高度 单位 磅 【需提前设置过纸张大小、页边距，不设置返回默认，获取不到返回0】
	 */
	public static int getPagebodyHeight(XWPFDocument docxDocument) {
		try {
			// 下面这个单位（缇）是英寸 * 1440 得到的， 一英寸 = 914400 EMUs
			CTSectPr sectPr = docxDocument.getDocument().getBody().getSectPr();
			if (sectPr != null) {
				int pageSize = (Integer) sectPr.getPgSz().getH(); // 纸张大小
				int topMar = (Integer) sectPr.getPgMar().getTop(); // 上缩进
				int bottomMar = (Integer) sectPr.getPgMar().getBottom(); // 下缩进
				return (pageSize - topMar - bottomMar) / 20;
			}
			else {
				return WordBaseConstants.PAPER_A4_H - 90 - 90; // 不设置纸张大小和边距的情况下默认高度
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}

	private static CTPageMar getCTPageMar(XWPFDocument document) {
		CTBody body = document.getDocument().getBody();
		CTSectPr sectPr = body.isSetSectPr() ? body.getSectPr() : body.addNewSectPr();
		CTPageMar ctpagemar = sectPr.isSetPgMar() ? sectPr.getPgMar() : sectPr.addNewPgMar();
		return ctpagemar;
	}

	/**
	 * 段落后拼接页码
	 * @param prefix 页码前缀 如 "第 1 页" 的 "第"
	 * @param suffix 页码后缀 如 "第 1 页" 的 "页"
	 */
	public static void appendPageNumber(XWPFParagraph paragraph, String fontFamily, int fontSize, String color,
			String prefix, String suffix) {
		if (StringUtil.isEmpty(color)) {
			color = "000000";
		}
		WordUtil.setStyle(paragraph.createRun(), fontFamily, fontSize, false, prefix, color);

		CTFldChar fldChar = paragraph.createRun().getCTR().addNewFldChar();
		fldChar.setFldCharType(STFldCharType.Enum.forString("begin"));

		XWPFRun numberRun = paragraph.createRun();
		CTText ctText = numberRun.getCTR().addNewInstrText();
		ctText.setStringValue("PAGE  \\* MERGEFORMAT");
		ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve"));
		WordUtil.setStyle(numberRun, fontFamily, fontSize, false, null, color);

		fldChar = paragraph.createRun().getCTR().addNewFldChar();
		fldChar.setFldCharType(STFldCharType.Enum.forString("end"));

		WordUtil.setStyle(paragraph.createRun(), fontFamily, fontSize, false, suffix, color);
	}

	/**
	 * 设置页眉页脚缩进
	 * @param headerMar 页眉距离顶端距离 单位 磅
	 * @param footerMar 页脚距离底部距离 单位 磅
	 */
	public static void setHeaderFooterMar(XWPFDocument document, float headerMar, float footerMar) {
		// 一磅 = 20 缇
		CTBody body = document.getDocument().getBody();
		CTSectPr sectPr = body.isSetSectPr() ? body.getSectPr() : body.addNewSectPr();
		CTPageMar pgMar = sectPr.isSetPgMar() ? sectPr.getPgMar() : sectPr.addNewPgMar();
		if (headerMar >= 0) {
			pgMar.setHeader(Math.round(headerMar * 20));
		}
		if (footerMar >= 0) {
			pgMar.setFooter(Math.round(footerMar * 20));
		}
	}

	/**
	 * 获取页眉高度 磅
	 */
	public static int getHeaderHeight(XWPFDocument document) {
		try {
			return (Integer) document.getDocument().getBody().getSectPr().getPgMar().getHeader() / 20;
		}
		catch (Exception e) {
			return 0;
		}
	}

	/**
	 * 获取页脚高度 磅
	 */
	public static int getFooterHeight(XWPFDocument document) {
		try {
			return (Integer) document.getDocument().getBody().getSectPr().getPgMar().getFooter() / 20;
		}
		catch (Exception e) {
			return 0;
		}
	}

	/**
	 * 设置单元格背景色。
	 */
	public static void setCellBackGround(XWPFTableCell cell, String color) {
		CTTc ctTc = cell.getCTTc();
		CTTcPr tcPr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
		CTShd ctShd = tcPr.isSetShd() ? tcPr.getShd() : tcPr.addNewShd();
		ctShd.setVal(STShd.CLEAR);
		ctShd.setFill(color);
	}

	/** 设置单元格宽度 单位 磅 */
	public static void setCellW(XWPFTableCell cell, float w) {
		CTTc ctTc = cell.getCTTc();
		CTTcPr tcPr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
		CTTblWidth tcW = tcPr.isSetTcW() ? tcPr.getTcW() : tcPr.addNewTcW();
		tcW.setType(STTblWidth.DXA);
		tcW.setW(BigInteger.valueOf((int) (w * 20f)));
	}

	/** 获取单元格宽度 单位 磅 */
	public static int getCellW(XWPFTableCell cell) {
		try {
			return Integer.parseInt(cell.getCTTc().getTcPr().getTcW().getW().toString()) / 20;
		}
		catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	/** 设置表格行 的 高度 单位 磅 。 */
	public static void setRowH(XWPFTableRow row, float h) {
		row.setHeight(Math.round(h * 20));
	}

	/** 获取表格行 的 高度 单位 磅 。 */
	public static int getRowH(XWPFTableRow row) {
		return row.getHeight() / 20;
	}

	/** 移除单元格 */
	public static void removeCell(XWPFTable table, int rows, int cols) {
		XWPFTableRow row = table.getRow(rows);

		XWPFTableCell cell = row.getCell(cols);
		List<XWPFParagraph> paragraphs = cell.getParagraphs();
		for (int p = 0; p < paragraphs.size(); p++) {
			cell.removeParagraph(p);
		}

		row.removeCell(cols);

		row.getCtRow().removeTc(cols);
	}

	/** 复刻单元格 */
	public static void copyCell(XWPFTableCell from, XWPFTableCell to) {
		to.getCTTc().getTcPr().set(from.getCTTc().getTcPr());
		if (StringUtil.isEmpty(to.getParagraphArray(0).getText())) {
			to.removeParagraph(0);
		}
		for (XWPFParagraph p : from.getParagraphs()) {
			to.addParagraph().getCTP().set(p.getCTP());
		}
	}

	/**
	 * 对应表格 绘制表格，用于给单元格 左上-右下 斜线分割
	 */
	public static void setDrawBias(XWPFTableCell cell) {
		CTTcPr tcPr = getCttcpr(cell);
		CTTcBorders ctTcBorders = tcPr.isSetTcBorders() ? tcPr.getTcBorders() : tcPr.addNewTcBorders();
		CTBorder ctBorder = ctTcBorders.isSetTl2Br() ? ctTcBorders.getTl2Br() : ctTcBorders.addNewTl2Br();
		ctBorder.setVal(STBorder.SINGLE);
		ctBorder.setSz(BigInteger.valueOf(4));
		ctBorder.setSpace(BigInteger.valueOf(0));
		ctBorder.setColor("auto");
	}

	/**
	 * @Description 设置单元格左边框样式
	 * @param cell 单元格
	 * @param borderType 边框类型
	 * @param borderW 边框宽度 磅
	 * @param space 左间隔
	 * @param color 边框颜色
	 */
	public static void setCellLeftBorder(XWPFTableCell cell, STBorder.Enum borderType, float borderW, float space,
			String color) {
		CTTcBorders ctTcBorders = getCttcBorders(cell);
		CTBorder leftBorder = ctTcBorders.isSetLeft() ? ctTcBorders.getLeft() : ctTcBorders.addNewLeft();
		leftBorder.setVal(borderType);
		leftBorder.setSz(BigInteger.valueOf(Math.round(borderW * 8)));
		leftBorder.setSpace(BigInteger.valueOf((int) space));
		leftBorder.setColor(color);
	}

	/**
	 * @Description 设置单元格上边框样式
	 * @param cell 单元格
	 * @param borderType 边框类型
	 * @param borderW 边框宽度 磅
	 * @param space 左间隔
	 * @param color 边框颜色
	 */
	public static void setCellTopBorder(XWPFTableCell cell, STBorder.Enum borderType, float borderW, float space,
			String color) {
		CTTcBorders ctTcBorders = getCttcBorders(cell);
		CTBorder topBorder = ctTcBorders.isSetTop() ? ctTcBorders.getTop() : ctTcBorders.addNewTop();
		topBorder.setVal(borderType);
		topBorder.setSz(BigInteger.valueOf(Math.round(borderW * 8)));
		topBorder.setSpace(BigInteger.valueOf((int) space));
		topBorder.setColor(color);
	}

	/**
	 * @Description 设置单元格右边框样式
	 * @param cell 单元格
	 * @param borderType 边框类型
	 * @param borderW 边框宽度 磅
	 * @param space 左间隔
	 * @param color 边框颜色
	 */
	public static void setCellRightBorder(XWPFTableCell cell, STBorder.Enum borderType, float borderW, float space,
			String color) {
		CTTcBorders ctTcBorders = getCttcBorders(cell);
		CTBorder rightBorder = ctTcBorders.isSetRight() ? ctTcBorders.getRight() : ctTcBorders.addNewRight();
		rightBorder.setVal(borderType);
		rightBorder.setSz(BigInteger.valueOf(Math.round(borderW * 8)));
		rightBorder.setSpace(BigInteger.valueOf((int) space));
		rightBorder.setColor(color);
	}

	/**
	 * @Description 设置单元格下边框样式
	 * @param cell 单元格
	 * @param borderType 边框类型
	 * @param borderW 边框宽度 磅
	 * @param space 左间隔
	 * @param color 边框颜色
	 */
	public static void setCellBottomBorder(XWPFTableCell cell, STBorder.Enum borderType, float borderW, float space,
			String color) {
		CTTcBorders ctTcBorders = getCttcBorders(cell);
		CTBorder bottomBorder = ctTcBorders.isSetBottom() ? ctTcBorders.getBottom() : ctTcBorders.addNewBottom();
		bottomBorder.setVal(borderType);
		bottomBorder.setSz(BigInteger.valueOf(Math.round(borderW * 8)));
		bottomBorder.setSpace(BigInteger.valueOf((int) space));
		bottomBorder.setColor(color);
	}

	/** 允许表格跨页断行，一个单元格里面的内容是否允许出现在两页上 */
	public static void setTableSplit(XWPFTable table, boolean isSplit) {
		List<XWPFTableRow> rows = table.getRows();
		for (XWPFTableRow row : rows) {
			setRowSplit(row, isSplit);
		}
	}

	/** 是否允许该行 跨页断行 ，一个单元格里面的内容是否允许出现在两页上 */
	public static void setRowSplit(XWPFTableRow row, boolean isSplit) {
		CTRow ctRow = row.getCtRow();
		CTTrPr trPr = ctRow.isSetTrPr() ? ctRow.getTrPr() : ctRow.addNewTrPr();
		if (isSplit) {
			if (trPr.sizeOfCantSplitArray() == 0) {
				trPr.addNewCantSplit();
			}
		}
		else {
			for (int i = 0; i < trPr.sizeOfCantSplitArray(); i++) {
				trPr.removeCantSplit(i);
			}
		}
	}

	/**
	 * @Description 设置单元格边距
	 * @param cell 待设置的单元格
	 * @param left 左边距 磅
	 * @param top 上边距 磅
	 * @param right 右边距 磅
	 * @param bottom 下边距 磅
	 */
	public static void setTableCellMar(XWPFTableCell cell, float left, float top, float right, float bottom) {
		CTTcPr cttcpr = getCttcpr(cell);
		CTTcMar ctTcMar = cttcpr.isSetTcMar() ? cttcpr.getTcMar() : cttcpr.addNewTcMar();
		if (left >= 0) {
			(ctTcMar.isSetLeft() ? ctTcMar.getLeft() : ctTcMar.addNewLeft()).setW(Math.round(left * 20));
		}
		if (top >= 0) {
			(ctTcMar.isSetTop() ? ctTcMar.getTop() : ctTcMar.addNewTop()).setW(Math.round(top * 20));
		}
		if (right >= 0) {
			(ctTcMar.isSetRight() ? ctTcMar.getRight() : ctTcMar.addNewRight()).setW(Math.round(right * 20));
		}
		if (bottom >= 0) {
			(ctTcMar.isSetBottom() ? ctTcMar.getBottom() : ctTcMar.addNewBottom()).setW(Math.round(bottom * 20));
		}
	}

	/**
	 * 跨列合并，横着合并 , 都是下标值
	 * @param rowindex 要合并哪一行的列
	 * @param fromcell 从哪列开始合并（下标）
	 * @param endcell 合并到哪列结束（下标）
	 */
	public static void mergeCellsByCol(XWPFTable table, int rowindex, int fromcell, int endcell) {
		// 2013及之前的老版本自己合并自己会出乱版，这里防止自己合并自己
		if (fromcell == endcell) {
			return;
		}
		for (int cellindex = fromcell; cellindex <= endcell; cellindex++) {
			XWPFTableCell cell = table.getRow(rowindex).getCell(cellindex);
			CTTc ctTc = cell.getCTTc();
			CTTcPr tcPr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
			CTHMerge hMerge = tcPr.isSetHMerge() ? tcPr.getHMerge() : tcPr.addNewHMerge();
			if (cellindex == fromcell) {
				// 这个判断是为了处理 2 3 合并 之后又 3 4 合并，如果不做判断最终合并的只有3 4
				if (!STMerge.CONTINUE.equals(hMerge.getVal())) {
					hMerge.setVal(STMerge.RESTART);
				}
			}
			else {
				hMerge.setVal(STMerge.CONTINUE);
			}
		}
	}

	/**
	 * 跨行合并 , 都是下标值
	 * @param cellindex 要合并哪一列的行
	 * @param fromrow 从哪行开始合并（下标）
	 * @param endrow 合并到哪行结束（下标）
	 */
	public static void mergeCellByRow(XWPFTable table, int cellindex, int fromrow, int endrow) {
		// 2013及之前的老版本自己合并自己会出乱版，这里防止自己合并自己
		if (fromrow == endrow) {
			return;
		}
		for (int rowindex = fromrow; rowindex <= endrow; rowindex++) {
			XWPFTableRow row = table.getRow(rowindex);
			CTTc ctTc = row.getCell(cellindex).getCTTc();
			CTTcPr tcPr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
			CTVMerge vMerge = tcPr.isSetVMerge() ? tcPr.getVMerge() : tcPr.addNewVMerge();
			if (rowindex == fromrow) {
				if (!STMerge.CONTINUE.equals(vMerge.getVal())) {
					vMerge.setVal(STMerge.RESTART);
				}
			}
			else {
				vMerge.setVal(STMerge.CONTINUE);
			}
		}
	}

	/**
	 * 检查当前单元格是否是合并后的单元格
	 * @Return boolean
	 */
	public static boolean isMergedCell(XWPFTableCell cell) {
		CTTcPr cttcpr = getCttcpr(cell);
		if (cttcpr.isSetVMerge()) {
			CTVMerge vMerge = cttcpr.getVMerge();
			if (STMerge.CONTINUE.equals(vMerge.getVal())) {
				return true;
			}
		}
		if (cttcpr.isSetHMerge()) {
			CTHMerge hMerge = cttcpr.getHMerge();
			if (STMerge.CONTINUE.equals(hMerge.getVal())) {
				return true;
			}
		}
		return false;
	}

	/** 获取边框对象 */
	private static CTTcBorders getCttcBorders(XWPFTableCell cell) {
		CTTcPr ctTcPr = getCttcpr(cell);
		return ctTcPr.isSetTcBorders() ? ctTcPr.getTcBorders() : ctTcPr.addNewTcBorders();
	}

	private static CTTcPr getCttcpr(XWPFTableCell cell) {
		CTTc ctTc = cell.getCTTc();
		return ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
	}

	/**
	 * 是否允许表格自动重调单元格宽度 对应【表格属性-表格-选项-自动重调尺寸以适应内容】
	 */
	public static void setTableWidthFixed(XWPFTable table, boolean isFixed) {
		CTTblPr tblPr = table.getCTTbl().getTblPr();
		CTTblLayoutType tblLayout = tblPr.isSetTblLayout() ? tblPr.getTblLayout() : tblPr.addNewTblLayout();
		tblLayout.setType(STTblLayoutType.FIXED);
	}

	/**
	 * 设置表格高度
	 */
	public static void setTableHight_pt(XWPFTable table, int hight) {
		List<XWPFTableRow> rows2 = table.getRows();
		for (XWPFTableRow xwpfTableRow : rows2) {
			setRowH(xwpfTableRow, hight / rows2.size());
		}
	}

	/**
	 * 若表格位列两页，则被分开的表格都有一个头部行
	 */
	public static void setHeaderRepetition(XWPFTableRow headerRow) {
		headerRow.setRepeatHeader(true); // 表头重复
	}

	/**
	 * 设置表格位置
	 * @param tblpX X方向右移的距离 磅
	 * @param tblpY Y方向下移的距离 磅
	 */
	public static void setTableSite(XWPFTable table, float tblpX, float tblpY) {
		CTTbl ctTbl = table.getCTTbl();
		CTTblPr tblPr = ctTbl.getTblPr();
		if (tblPr == null) {
			tblPr = ctTbl.addNewTblPr();
		}
		CTTblPPr ctTblPPr = tblPr.isSetTblpPr() ? tblPr.getTblpPr() : tblPr.addNewTblpPr();
		ctTblPPr.setVertAnchor(STVAnchor.TEXT); // 定位：随文字移动
		ctTblPPr.setTblpX(Math.round(tblpX * 20));
		ctTblPPr.setTblpY(Math.round(tblpY * 20));
	}

	/**
	 * 抹除表格边框
	 */
	public static void unsetTableBorder(XWPFTable table) {
		table.setLeftBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
		table.setRightBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
		table.setTopBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
		table.setBottomBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
		table.setInsideHBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
		table.setInsideVBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
	}

	/**
	 * 恢复所有表格边框 默认值
	 */
	public static void setDefalutTableBorder(XWPFTable table) {
		table.setLeftBorder(XWPFTable.XWPFBorderType.SINGLE, 0, 0, "");
		table.setTopBorder(XWPFTable.XWPFBorderType.SINGLE, 0, 0, "");
		table.setRightBorder(XWPFTable.XWPFBorderType.SINGLE, 0, 0, "");
		table.setBottomBorder(XWPFTable.XWPFBorderType.SINGLE, 0, 0, "");
		table.setInsideHBorder(XWPFTable.XWPFBorderType.SINGLE, 0, 0, ""); // 单元格底边框
		table.setInsideVBorder(XWPFTable.XWPFBorderType.SINGLE, 0, 0, ""); // 单元格右边框
	}

	/**
	 * 设置table边框线样式
	 * @param style {@link XWPFTable.XWPFBorderType#NONE}
	 * @param borderW 边框宽度 磅
	 * @param color 颜色值：如 ffffff ，注意不能带 # 号
	 */
	public static void setTableBorder(XWPFTable table, XWPFTable.XWPFBorderType style, float borderW, String color)
			throws Exception {

		int w = Math.round(borderW * 8);
		table.setLeftBorder(style, w, 0, color);
		table.setTopBorder(style, w, 0, color);
		table.setRightBorder(style, w, 0, color);
		table.setBottomBorder(style, w, 0, color);
		// 注意一下两个方法有限制范围
		table.setInsideHBorder(style, w, 0, color); // 单元格底边框
		table.setInsideVBorder(style, w, 0, color); // 单元格右边框
		// 该效果的边框需要添加一定的单元格间距
		if (style == XWPFTable.XWPFBorderType.OUTSET || style == XWPFTable.XWPFBorderType.INSET) {
			CTTblPr tblPr = table.getCTTbl().getTblPr();
			CTTblWidth ctTblWidth = tblPr.isSetTblCellSpacing() ? tblPr.getTblCellSpacing()
					: tblPr.addNewTblCellSpacing();
			ctTblWidth.setW(20);
			ctTblWidth.setType(STTblWidth.DXA);
		}
	}

	/**
	 * 列宽自动分割
	 * @param tableWidth 表格宽度 磅
	 * @param fixedWidth 单元格宽度是否固定，固定则不会随单元格内文字多少而改变
	 */
	public static void setTableWidth_pt(XWPFTable table, float tableWidth, boolean fixedWidth) {
		CTTblPr tblPr = getCTTblPr(table);
		CTTblWidth comTableWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
		comTableWidth.setType(STTblWidth.DXA);
		comTableWidth.setW(Math.round(tableWidth * 20));
		List<XWPFTableRow> rows2 = table.getRows();
		for (XWPFTableRow xwpfTableRow : rows2) {
			xwpfTableRow.setCantSplitRow(true);
			if (fixedWidth) {
				float cellW = tableWidth / xwpfTableRow.getTableCells().size();
				for (XWPFTableCell cell : xwpfTableRow.getTableCells()) {
					setCellW(cell, cellW);
				}
			}
		}
	}

	/**
	 * 设置表格样式为 黑白间隔样式
	 * @param beginRowIndex 从哪行开始设置(下标值)
	 * @param beginCellIndex 从哪列开始设置(下标值)
	 */
	public static void setTableStyle_BW(XWPFTable table, int beginRowIndex, int beginCellIndex) {
		List<XWPFTableRow> rows = table.getRows();
		for (int i = beginRowIndex + 1; i < rows.size(); i += 2) {
			XWPFTableRow row = rows.get(i);
			List<XWPFTableCell> cells = row.getTableCells();
			for (int c = beginCellIndex; c < cells.size(); c++) {
				XWPFTableCell cell = cells.get(c);
				if (StringUtil.isEmpty(cell.getColor())) {
					cell.setColor("f1f0f3");
				}
			}
		}
	}

	/**
	 * 设置表格水平对齐方式
	 * @param align {@link STJcTable#CENTER}
	 */
	public static void setTableAlign(XWPFTable table, STJcTable.Enum align) {
		// 表格总体
		CTTblPr tblPr = getCTTblPr(table);
		CTJcTable ctJcTable = tblPr.isSetJc() ? tblPr.getJc() : tblPr.addNewJc();
		ctJcTable.setVal(align);

		// 表格各行
		List<XWPFTableRow> rows = table.getRows();
		for (XWPFTableRow row : rows) {
			CTRow ctRow = row.getCtRow();
			CTTrPr ctTrPr = ctRow.isSetTrPr() ? ctRow.getTrPr() : ctRow.addNewTrPr();
			ctTrPr.addNewJc().setVal(align);
		}
	}

	/**
	 * 设置表格缩进 对应【表格属性-表格-左缩进】
	 * @param ind 左缩进值 磅
	 */
	public static void setTableInd(XWPFTable table, float ind) {
		CTTblPr tblPr = getCTTblPr(table);
		CTTblWidth ctTblWidth = tblPr.isSetTblInd() ? tblPr.getTblInd() : tblPr.addNewTblInd();
		ctTblWidth.setW(BigInteger.valueOf((int) (ind * 20f)));
		ctTblWidth.setType(STTblWidth.DXA);
	}

	/**
	 * @Description 设置表格内的所有单元格边距
	 * @param table 待设置的表格
	 * @param left 左边距 磅
	 * @param top 上边距 磅
	 * @param right 右边距 磅
	 * @param bottom 下边距 磅
	 */
	public static void setTableCellMar(XWPFTable table, float left, float top, float right, float bottom) {
		CTTblPr ctTblPr = getCTTblPr(table);
		CTTblCellMar ctTblCellMar = ctTblPr.isSetTblCellMar() ? ctTblPr.getTblCellMar() : ctTblPr.addNewTblCellMar();
		if (left >= 0) {
			(ctTblCellMar.isSetLeft() ? ctTblCellMar.getLeft() : ctTblCellMar.addNewLeft()).setW(Math.round(left * 20));
		}
		if (top >= 0) {
			(ctTblCellMar.isSetTop() ? ctTblCellMar.getTop() : ctTblCellMar.addNewTop()).setW(Math.round(top * 20));
		}
		if (right >= 0) {
			(ctTblCellMar.isSetRight() ? ctTblCellMar.getRight() : ctTblCellMar.addNewRight())
					.setW(Math.round(right * 20));
		}
		if (bottom >= 0) {
			(ctTblCellMar.isSetBottom() ? ctTblCellMar.getBottom() : ctTblCellMar.addNewBottom())
					.setW(Math.round(bottom * 20));
		}
	}

	private static CTTblPr getCTTblPr(XWPFTable table) {
		CTTbl ctTbl = table.getCTTbl();
		CTTblPr tblPr = ctTbl.getTblPr();
		if (tblPr == null) {
			tblPr = ctTbl.addNewTblPr();
		}
		return tblPr;
	}

	// 插入分页符
	public static XWPFParagraph setPageBreak(XWPFDocument document) {
		XWPFParagraph pageParagraph = document.createParagraph();
		pageParagraph.setPageBreak(true);
		return pageParagraph;
	}

	// 插入段落换行符
	public static void setParagraphBreak(XWPFParagraph paragraph) {
		paragraph.createRun().addBreak();
	}

	/**
	 * 设置段落内文本上下对齐方式【段落-中文板式-文本对齐方式】
	 * @param textAlignment {@link STTextAlignment#CENTER}
	 */
	public static void setTextAlignment(XWPFParagraph paragraph, STTextAlignment.Enum textAlignment) {
		CTP ctp = paragraph.getCTP();
		CTPPr ctpPr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();
		CTTextAlignment ctTextAlignment = CTTextAlignment.Factory.newInstance();
		ctTextAlignment.setVal(textAlignment);
		ctpPr.setTextAlignment(ctTextAlignment);
	}

	/**
	 * 调整段落间距【对应word段落设置-间距设置】
	 * @param before 段前 磅
	 * @param after 段后 磅
	 * @param multiple 几倍行距
	 */
	public static void setParagraphSpacing(XWPFParagraph paragraph, float before, float after, float multiple) {
		CTPPr ppr = paragraph.getCTP().getPPr();
		if (ppr == null) {
			ppr = paragraph.getCTP().addNewPPr();
		}
		CTSpacing spacing = ppr.isSetSpacing() ? ppr.getSpacing() : ppr.addNewSpacing();
		spacing.setBefore(Math.round(before * 20));
		spacing.setAfter(Math.round(after * 20));
		spacing.setLine(Math.round(240 * multiple));
		spacing.setLineRule(STLineSpacingRule.AUTO);
	}

	/**
	 * 固定行高
	 * @param h 行高 磅
	 */
	public static void setParagraphHight(XWPFParagraph paragraph, float h) {
		CTPPr ppr = paragraph.getCTP().getPPr();
		if (ppr == null) {
			ppr = paragraph.getCTP().addNewPPr();
		}
		CTSpacing spacing = ppr.isSetSpacing() ? ppr.getSpacing() : ppr.addNewSpacing();
		spacing.setBefore(BigInteger.valueOf(0));
		spacing.setAfter(BigInteger.valueOf(0));
		spacing.setLine(BigInteger.valueOf((long) (20 * h)));
		spacing.setLineRule(STLineSpacingRule.EXACT);
	}

	/**
	 * 段落设置-中文板式 ： 允许西文在单词中间换行
	 */
	public static void setParagraphWrap(XWPFParagraph paragraph, boolean allow) {
		// 解释： 允许后（勾选） word展示效果
		// | 这是一个段落， | |带来了珍贵的礼物—— |
		// |paragraph | | 五彩缤纷的落叶。 |

		// 不允许（取消勾选） word展示效果
		// | 这是一个段落，par| |带来了珍贵的礼物——五彩缤 |
		// |agraph | |纷的落叶。 |

		CTP ctp = paragraph.getCTP();
		CTPPr ctpPr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();
		CTOnOff ctOnOff = ctpPr.isSetWordWrap() ? ctpPr.getWordWrap() : ctpPr.addNewWordWrap();
		if (allow) { // 允许
			ctOnOff.setVal(STOnOff.EQUAL);
		}
		else { // 不允许
			ctpPr.unsetWordWrap();
		}
	}

	/**
	 * 设置段落缩进
	 * @param left 左缩进值 磅
	 * @param right 右缩进值 磅
	 */
	public static void setParagraphInd(XWPFParagraph paragraph, float left, float right) {
		CTPPr ppr = paragraph.getCTP().getPPr();
		if (ppr == null) {
			ppr = paragraph.getCTP().addNewPPr();
		}
		CTInd ctInd = ppr.isSetInd() ? ppr.getInd() : ppr.addNewInd();
		if (left >= 0) {
			ctInd.setLeft(Math.round(left * 20));
		}
		if (right >= 0) {
			ctInd.setRight(Math.round(right * 20));
		}
	}

	/**
	 * 设置 段落内 文本位置【字符间距】
	 * @param positionValue 【上下位置调整 正 上移 负 下移】 单位 磅
	 * @param spacingValue 【字符间距调整 宽窄-正负】单位 磅
	 */
	public static void setPosition_paragraph(XWPFParagraph paragraph, int positionValue, int spacingValue) {
		List<XWPFRun> runs = paragraph.getRuns();
		for (XWPFRun run : runs) {
			setPosition_run(run, positionValue, spacingValue);
		}
	}

	/**
	 * 设置文段Run位置【对应word字体 - 高级 - 字符间距】
	 * @param positionValue 【上下位置调整正负】 单位 磅
	 * @param spacingValue 【字符间距调整 宽窄-正负】单位 磅
	 */
	public static void setPosition_run(XWPFRun run, float positionValue, float spacingValue) {
		CTRPr rPr = run.getCTR().getRPr();
		if (rPr == null) {
			rPr = run.getCTR().addNewRPr();
		}
		CTSignedHpsMeasure position = rPr.sizeOfPositionArray() > 0 ? rPr.getPositionArray(0) : null;
		if (position == null) {
			rPr.addNewPosition().setVal(positionValue * 2);
		}
		else {
			position.setVal(positionValue * 2);
		}
		CTSignedTwipsMeasure spacing = rPr.sizeOfSpacingArray() > 0 ? rPr.getSpacingArray(0) : null;
		if (spacing == null) {
			rPr.addNewSpacing().setVal(spacingValue * 20);
		}
		else {
			spacing.setVal(spacingValue * 20);
		}
	}

	/**
	 * 设置整个段落内样式
	 * @param fontFamily 字体
	 * @param fontSize 字体大小 磅
	 * @param bold 是否加粗
	 * @param color 字体颜色
	 */
	public static void setStyle(XWPFParagraph paragraph, String fontFamily, int fontSize, boolean bold, String color) {
		CTP ctp1 = paragraph.getCTP();
		CTPPr pPr = ctp1.isSetPPr() ? ctp1.getPPr() : ctp1.addNewPPr();
		CTParaRPr rPr = pPr.isSetRPr() ? pPr.getRPr() : pPr.addNewRPr();

		CTFonts ctFonts = rPr.sizeOfRFontsArray() > 0 ? rPr.getRFontsArray(0) : rPr.addNewRFonts();
		ctFonts.setAscii(fontFamily);
		ctFonts.setEastAsia(fontFamily);
		ctFonts.setHAnsi(fontFamily);
		CTHpsMeasure sz = rPr.sizeOfSzArray() > 0 ? rPr.getSzArray(0) : rPr.addNewSz();
		sz.setVal(BigInteger.valueOf(fontSize * 2L));
		if (bold) {
			rPr.addNewB();
		}
		else {
			if (rPr.sizeOfBArray() > 0) {
				rPr.removeB(0);
			}
		}
		if (!StringUtil.isEmpty(color)) {
			CTColor ctColor = rPr.sizeOfColorArray() > 0 ? rPr.getColorArray(0) : rPr.addNewColor();
			ctColor.setVal(color);
		}
	}

	/**
	 * 设置样式
	 */
	public static void setStyle(XWPFRun run, String fontFamily, float fontSize, boolean bold, String text,
			String color) {
		setStyle(run, fontFamily, fontSize, bold, text, color, false, false);
	}

	public static void setStyle(XWPFRun run, String fontFamily, float fontSize, boolean bold, String text, String color,
			boolean em, boolean i) {
		run.setBold(bold);
		run.setFontFamily(fontFamily);
		run.setFontSize(fontSize);
		if (text != null) {
			run.setText(text);
		}
		run.setColor(StringUtil.isEmpty(color) ? "000000" : color); // 颜色默认黑色
		// 着重号
		if (em) {
			run.getCTR().getRPr().addNewEm().setVal(STEm.UNDER_DOT);
		}
		if (i) {
			run.setItalic(true);
		}
	}

	public static void setStyle(XWPFRun run, String fontFamily, float fontSize, boolean bold, String text, String color,
			STUnderline.Enum underType, String underColor) {
		setStyle(run, fontFamily, fontSize, bold, text, color, false, false, underType, underColor);
	}

	/**
	 * 设置单个run的样式
	 * @param run
	 * @param fontFamily 字体
	 * @param fontSize 文字大小 磅
	 * @param bold 是否加粗
	 * @param text run内文本
	 * @param color 文本颜色
	 * @param em 是否添加着重号
	 * @param i 是否倾斜
	 * @param underType 下划线类型{@link STUnderline#SINGLE}
	 * @param underColor 下划线颜色
	 *
	 */
	public static void setStyle(XWPFRun run, String fontFamily, float fontSize, boolean bold, String text, String color,
			boolean em, boolean i, STUnderline.Enum underType, String underColor) {
		setStyle(run, fontFamily, fontSize, bold, text, color, em, i);

		CTR ctr = run.getCTR();
		CTRPr ctrPr = ctr.isSetRPr() ? ctr.getRPr() : ctr.addNewRPr();
		CTUnderline ctUnderline = ctrPr.sizeOfUArray() > 0 ? ctrPr.getUArray(0) : ctrPr.addNewU();
		ctUnderline.setVal(underType);
		if (!StringUtil.isEmpty(underColor)) {
			ctUnderline.setColor(underColor);
		}
	}

	/**
	 * 设置文本填充色
	 */
	public static void setFillColor(XWPFRun run, String color) {
		CTR ctr = run.getCTR();
		CTRPr rPr = ctr.isSetRPr() ? ctr.getRPr() : ctr.addNewRPr();
		CTShd ctShd = rPr.sizeOfShdArray() > 0 ? rPr.getShdArray(0) : rPr.addNewShd();
		ctShd.setFill(color);
	}

	/** 移除一个段落或者表格 */
	public static void removeIBodyElement(XWPFDocument document, IBodyElement iBodyElement) {
		if (iBodyElement != null) {
			if (iBodyElement.getElementType() == BodyElementType.PARAGRAPH) { // 段落
				removeParagraph(document, ((XWPFParagraph) iBodyElement));
			}
			else if (iBodyElement.getElementType() == BodyElementType.TABLE) { // 表格
				removeTable(document, (XWPFTable) iBodyElement);
			}
		}
	}

	/** 移除一个表格 未测试 */
	private static void removeTable(XWPFDocument document, XWPFTable table) {
		// 获取表格所在的段落
		XWPFParagraph paragraph = document.createParagraph();

		// 获取段落的索引
		int paraPos = document.getPosOfParagraph(paragraph);

		// 获取表格的索引
		int tablePos = document.getTables().indexOf(table);

		// 移除段落
		document.removeBodyElement(paraPos);

		// 移除表格
		document.removeBodyElement(tablePos);
	}

	/** 移除一个段落 这个看上去就有问题 */
	private static void removeParagraph(XWPFDocument document, XWPFParagraph iBodyElement) {
		// 获取表格所在的段落
		XWPFParagraph paragraph = document.createParagraph();
		// 获取段落所在的索引
		int paraPos = document.getPosOfParagraph(paragraph);

		// 移除段落
		document.removeBodyElement(paraPos);
	}

	/** 移除单元格对应的边框线 */
	public static void removeCellLeftBorder(XWPFTableCell cell) {
		cell.getCTTc().getTcPr().addNewTcBorders().addNewLeft().setVal(STBorder.NIL);
	}

	public static void removeCellRightBorder(XWPFTableCell cell) {
		cell.getCTTc().getTcPr().addNewTcBorders().addNewRight().setVal(STBorder.NIL);
	}

	public static void removeCellTopBorder(XWPFTableCell cell) {
		cell.getCTTc().getTcPr().addNewTcBorders().addNewTop().setVal(STBorder.NIL);
	}

	public static void removeCellBottomBorder(XWPFTableCell cell) {
		cell.getCTTc().getTcPr().addNewTcBorders().addNewBottom().setVal(STBorder.NIL);
	}

	/** 移除单元格 左 边框线 */
	public static void removeCellLeftBorder2(XWPFTableCell cell) {
		setCellLeftBorder(cell, STBorder.NIL, 0, 0, "");
	}

	/** 移除单元格 右 边框线 */
	public static void removeCellRightBorder2(XWPFTableCell cell) {
		setCellRightBorder(cell, STBorder.NIL, 0, 0, "");
	}

	/** 移除单元格 上 边框线 */
	public static void removeCellTopBorder2(XWPFTableCell cell) {
		setCellTopBorder(cell, STBorder.NIL, 0, 0, "");
	}

	/** 移除单元格 下 边框线 */
	public static void removeCellBottomBorder2(XWPFTableCell cell) {
		setCellBottomBorder(cell, STBorder.NIL, 0, 0, "");
	}

	// ============================================第四部分======================================================================

}
